package com.gitee.wsl.compose.chart.frond

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.drawscope.Stroke
import com.gitee.wsl.common.ui.draw.style.line1
import com.gitee.wsl.common.ui.ext.path.lineTo
import com.gitee.wsl.common.ui.ext.path.moveTo
import com.gitee.wsl.unit.angle.AngleUnit
import com.gitee.wsl.unit.angle.deg
import com.gitee.wsl.mathematics.coordinate.polar.ext.polarToCartesian
import kotlin.math.pow
import kotlin.math.sqrt

typealias LabelSlate = @Composable (Int) -> Unit

typealias LabelConnectorSlate = @Composable LabelConnectorScope.(Int) -> Unit

object LabelConnector

/**
 * The LabelConnectorScope provides geometry information to LabelConnector implementations so they
 * can draw the connector from the graph element to the label.
 */
 interface LabelConnectorScope {
    /**
     * The starting position of the connector at the graph element.
     */
     val startPosition: MutableState<Offset>

    /**
     * The ending position of the connector at the label.
     */
     val endPosition: MutableState<Offset>

    /**
     * The angle of the graph element at the start of the connector. For a Pie chart this is
     * the angle of the slice it connects to.
     */
     val startAngle: MutableState<AngleUnit>

    /**
     * The angle of the label at the end of the connector. This is an angle that is normal to the
     * connector's bounding box at the [endPosition]
     */
     val endAngle: MutableState<AngleUnit>
}

internal data class LabelConnectorScopeImpl(
    override val startPosition: MutableState<Offset> = mutableStateOf(Offset.Zero),
    override val endPosition: MutableState<Offset> = mutableStateOf(Offset.Zero),
    override val startAngle: MutableState<AngleUnit> = mutableStateOf(0.deg),
    override val endAngle: MutableState<AngleUnit> = mutableStateOf(0.deg)
) : LabelConnectorScope

@Composable
fun LabelConnector.Bezier(modifier: Modifier = Modifier,
                          connectorColor: Color = MaterialTheme.colorScheme.onBackground,
                          connectorStroke: Stroke = Stroke.line1):LabelConnectorSlate
                = { BezierLabelConnector(modifier, connectorColor, connectorStroke) }

/**
 * A label connector that uses a Bezier curve.
 *
 * @param connectorColor The color of the connector line.
 * @param connectorStroke The stroke used to draw the connector line.
 */
@Composable
 fun LabelConnectorScope.BezierLabelConnector(
    modifier: Modifier = Modifier,
    connectorColor: Color = MaterialTheme.colorScheme.onBackground,
    connectorStroke: Stroke = Stroke.line1
) {
    val length by remember(startPosition.value, endPosition.value) {
        val delta = startPosition.value - endPosition.value
        mutableStateOf(sqrt(delta.x.pow(2) + delta.y.pow(2)))
    }

    val path = Path().apply {
        moveTo(startPosition.value)

        // control point 1
        val cp1 = startPosition.value + polarToCartesian(length / 2, startAngle.value)

        // control point 2
        val cp2 = endPosition.value + polarToCartesian(length / 2, endAngle.value)

        cubicTo(cp1.x, cp1.y, cp2.x, cp2.y, endPosition.value.x, endPosition.value.y)
    }

    Box(
        modifier = modifier.fillMaxSize().drawBehind {
            drawPath(
                path = path,
                color = connectorColor,
                style = connectorStroke
            )
        }
    ) {}
}

@Composable
fun LabelConnector.Straight(modifier: Modifier = Modifier,
                          connectorColor: Color = MaterialTheme.colorScheme.onBackground,
                          connectorStroke: Stroke = Stroke.line1):LabelConnectorSlate
        = { StraightLineConnector(modifier, connectorColor, connectorStroke) }


/**
 * A label connector that uses a straight line.
 *
 * @param connectorColor The color of the connector line.
 * @param connectorStroke The stroke used to draw the connector line.
 */
@Composable
 fun LabelConnectorScope.StraightLineConnector(
    modifier: Modifier = Modifier,
    connectorColor: Color = MaterialTheme.colorScheme.onBackground,
    connectorStroke: Stroke = Stroke.line1
) {
    val path = Path().apply {
        moveTo(startPosition.value)
        lineTo(endPosition.value)
    }
    Box(
        modifier = modifier.fillMaxSize().drawBehind {
            drawPath(path = path, color = connectorColor, style = connectorStroke)
        }
    ) {}
}